/*
 *  Test.cpp
 *  
 *
 *  Created by Daniel Condon on 26/07/12.
 *  Copyright 2012 __MyCompanyName__. All rights reserved.
 *
 */


#include "CellularAutomation.h"

#include <GLUT/glut.h>
#include <OpenGL/glu.h>
#include <OpenGL/gl.h>
#include <OpenGL/OpenGL.h>

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>

using namespace std;

const int VIEW_DISTANCE = 500;
const int WINDOW_SIZE_X = 600;
const int WINDOW_SIZE_Y = 600;

double mouseXObjectSpace;
double mouseYObjectSpace;

bool KEY_STATES[256];

const int MatrixColumns = 50;
const int MatrixRows = 50;

const int seedCellx = 25;
const int seedCelly = 25;

double divisionWidth;
double divisionHeight;

bool PAUSED, DEBUG;

Creature creatures;

CellularAutomation* cellMatrix = new CellularAutomation(MatrixColumns, MatrixRows, creatures);

void convertMouseCoord(int x, int y){
	mouseXObjectSpace = (x - (WINDOW_SIZE_X/2.0f)) / (WINDOW_SIZE_X/2.0f) * (VIEW_DISTANCE/2.0f);
	mouseYObjectSpace = (-y + (WINDOW_SIZE_Y/2.0f)) / (WINDOW_SIZE_Y/2.0f) * (VIEW_DISTANCE/2.0f);
}

void Init(){
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glClear(GL_COLOR_BUFFER_BIT);
	glEnable(GL_CULL_FACE);
	glEnable (GL_BLEND);
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

void DrawCellBorders(){
	glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
	divisionWidth = VIEW_DISTANCE/MatrixColumns;
	divisionHeight = VIEW_DISTANCE/MatrixRows;
	glBegin(GL_LINES);
	
	for (int x = 0; x < MatrixColumns; x++) {
		glVertex2f(x*divisionWidth, 0);
		glVertex2f(x*divisionWidth, VIEW_DISTANCE);
	}
	for (int y = 0; y < MatrixRows; y++) {
		glVertex2f(0, y*divisionHeight);
		glVertex2f(VIEW_DISTANCE, y*divisionHeight);
	}
	glEnd();
}

void DrawBarriers(int w, int h){
	for (int i = 0; i < 4; i++) {
		if (cellMatrix->getCell(w,h)->hasBarrier(i)) {
			glColor4f(1.0,0.0,0.0,1.0);
			glBegin(GL_LINES);
			switch (i) {
				case 0:
					glVertex2f((w+1)*divisionWidth, h*divisionHeight);
					glVertex2f((w+1)*divisionWidth, (h+1)*divisionHeight);
					break;
				case 1:
					glVertex2f((w+1)*divisionWidth, (h+1)*divisionHeight);
					glVertex2f(w*divisionWidth, (h+1)*divisionHeight);
					break;
				case 2:
					glVertex2f(w*divisionWidth, (h+1)*divisionHeight);
					glVertex2f(w*divisionWidth, h*divisionHeight);
					break;
				case 3:
					glVertex2f(w*divisionWidth, h*divisionHeight);
					glVertex2f((w+1)*divisionWidth, h*divisionHeight);
					break;
				default:
					break;
			}
			glEnd();
		}
	}
	
}

void DrawCells(){
	divisionWidth = VIEW_DISTANCE/MatrixColumns;
	divisionHeight = VIEW_DISTANCE/MatrixRows;
	for (int w = 0; w < MatrixColumns; w++) {
		for (int h = 0; h < MatrixRows; h++) {
			if (cellMatrix->getCell(w,h)->getCellCurrent() > 0) {
				double fill = cellMatrix->getCell(w,h)->getCellCurrent()/cellMatrix->getCell(w,h)->getCellMax();
				if (fill > 1.0f) {
					glColor4f(1.0, 0.0, 0.0, fill);
				}else {
					glColor4f(1.0f, 1.0f, 1.0f, fill);
				}				
				glBegin(GL_QUADS);
				glVertex2f(w*divisionWidth, h*divisionHeight);
				glVertex2f((w+1)*divisionWidth, h*divisionHeight);
				glVertex2f((w+1)*divisionWidth, (h+1)*divisionHeight);
				glVertex2f(w*divisionWidth, (h+1)*divisionHeight);
				glEnd();
			}
			DrawBarriers(w, h);
		}
	}
}

void DrawSelectedCell(){
	divisionWidth = VIEW_DISTANCE/MatrixColumns;
	divisionHeight = VIEW_DISTANCE/MatrixRows;
	
	glBegin(GL_QUADS);
	glVertex2f(mouseXObjectSpace*divisionWidth, mouseYObjectSpace*divisionHeight);
	glVertex2f((mouseXObjectSpace+1)*divisionWidth, mouseYObjectSpace*divisionHeight);
	glVertex2f((mouseXObjectSpace+1)*divisionWidth, (mouseYObjectSpace+1)*divisionHeight);
	glVertex2f(mouseXObjectSpace*divisionWidth, (mouseYObjectSpace+1)*divisionHeight);
	glEnd();
}
	
void display(){
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0, VIEW_DISTANCE, 0, VIEW_DISTANCE);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glClear(GL_COLOR_BUFFER_BIT);
	
	DrawCellBorders();
	DrawCells();
	glColor4f(0.0, 0.9, 0.3, 1.0);
	
	if (DEBUG) {
		DrawSelectedCell();
	}
	
	glutSwapBuffers();
}

void reshape(int w, int h){
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(-VIEW_DISTANCE, VIEW_DISTANCE, -VIEW_DISTANCE, VIEW_DISTANCE);
	glutPostRedisplay();
}

void KeyboardController(GLubyte key, GLint x, GLint y){
	string put;
	switch ((char)key) {
		case '1':
			for (int r = 0; r < MatrixRows; r++) {
				for (int c = 0; c < MatrixColumns; c++) {
					cout << "Cell[" << r << "][" << c << "] has " << cellMatrix->getCellCurrent(r,c) << endl;
				}
			}
			break;
		case 's':
			cellMatrix->seedCell(seedCellx,seedCelly,50);
			break;
		case 'p':
			PAUSED = !PAUSED;
			break;
		case 'b':
			cout << "Entering barrier placement mode." << endl;
			if (!PAUSED) {
				PAUSED = !PAUSED;
			}
			cout << "Input sides to barricade: " << endl;
			cin >> put;
			if (put.length() > 0) {
				for (int i = 0; i < put.length(); i++) {
					switch (put.at(i)) {
						case 'a':
							cellMatrix->getCell(mouseXObjectSpace, mouseYObjectSpace)->PlaceBarrier(0);
							break;
						case 'b':
							cellMatrix->getCell(mouseXObjectSpace, mouseYObjectSpace)->PlaceBarrier(1);
							break;
						case 'c':
							cellMatrix->getCell(mouseXObjectSpace, mouseYObjectSpace)->PlaceBarrier(2);
							break;
						case 'd':
							cellMatrix->getCell(mouseXObjectSpace, mouseYObjectSpace)->PlaceBarrier(3);
							break;
						default:
							break;
					}
				}
			}
			break;
		case 't':
			DEBUG = !DEBUG;
			break;
		case 27:
			exit(1);
			break;
		default:
			break;
	}
}

void Clip(){
	mouseXObjectSpace = (int)((mouseXObjectSpace + 250)/10);
	mouseYObjectSpace = (int)((mouseYObjectSpace + 250)/10);
}

void mouseInput(int button, int state, int x, int y){
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
		convertMouseCoord(x, y);
		int clickX, clickY;
		Clip();
		clickX = (mouseXObjectSpace);
		clickY = (mouseYObjectSpace);
		cout << clickX << ":" << clickY << endl;
	}
}

void mouseMove(int x, int y){
	//convertMouseCoord(x, y);
}

void LogicLoop(int value){
	if (!PAUSED) {
		cellMatrix->Cycle();
	}
	glutPostRedisplay();
	glutTimerFunc(value, LogicLoop, value);
}

int main(int argc, char** argv){
	PAUSED = false;
	DEBUG = false;
	divisionWidth = VIEW_DISTANCE/MatrixColumns;
	divisionHeight = VIEW_DISTANCE/MatrixRows;
	glutInitWindowSize(WINDOW_SIZE_X, WINDOW_SIZE_Y);
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
	glutCreateWindow("Automata Tester");
	
	Init();
	glutKeyboardFunc(KeyboardController);
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutMouseFunc(mouseInput);
	glutPassiveMotionFunc(mouseMove);
	
	LogicLoop(1000);
	glutMainLoop();
	
	delete cellMatrix;
	return 0;
}